Skip to content

refactor(build): serve generated entries through VirtualModulesPlugin at dedicated virtual paths - #80

Merged
ScriptedAlchemy merged 4 commits into
mainfrom
refactor/virtual-generated-entries
Aug 31, 2026
Merged

refactor(build): serve generated entries through VirtualModulesPlugin at dedicated virtual paths#80
ScriptedAlchemy merged 4 commits into
mainfrom
refactor/virtual-generated-entries

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Owner-requested architecture reversal of the generated-source transport from #79: generated wrapper entries and registry modules are served from memory through Rspack's experiments.VirtualModulesPlugin (via @rslib/core's re-exported rspack) instead of being written as throwaway files into the staged output root. This is not a revert of #79 — every conformance invariant it introduced is preserved and now has direct regression coverage.

Virtual-path scheme

Every generated module keeps its deterministic, collision-safe path under the reserved namespace, but the path is now guaranteed-nonexistent (nothing ever writes it):

  • wrapper entries: <outputRoot>/.agent-bundle-virtual/<entry-name>-entry.mjs
  • registry modules: <outputRoot>/.agent-bundle-virtual/<entry-name>-<index>.mjs

Rslib validates source.entry existence against the real filesystem before Rspack exists, so the composed profile keys each entry on the authored program and the framework invariant hook (merged last, unreachable by the consumer tools hatch) redirects the lowered Rspack entry to the virtual wrapper path and registers the plugin. No real path is ever shadowed — the pre-#79 entryAnchor overlay of the framework's own module stays dead. inspect --bundler output remains deterministic (the resolved bundler config shows the virtual path; the composed lib config shows the authored source plus generatedEntry).

What was removed

  • mkdir/writeFile materialization before the build and the recursive rm cleanup in finally (build handles still close in finally).
  • The materialization-specific justification for pinning output.cleanDistPath: false. The pin itself stays: scripts, MCP entries, hooks, and MCP Apps build sequentially into one shared staged root, so a cleaning environment would delete sibling outputs — now proven by a test that seeds a sibling artifact and builds under a hostile cleanDistPath: true hatch.

Experimental-API tradeoff

Accepted design decision, documented inline: a narrow feature check (virtualModulesPluginConstructor) turns an upstream rename/removal of the experimental plugin into an actionable diagnostic instead of an opaque resolution failure.

Preserved #79 invariants (each with coverage)

Invariant Status
Reserved-specifier externals rejected (static + function-external build-time guard) unchanged, tests green
Residual reserved-import scan (es-module-lexer) of emitted bundles unchanged, tests green
Consumer alias shadowing of reserved specifiers rejected unchanged, tests green
ID-keyed inspection (environmentConfigs[lib.id], config name) unchanged; extended with plugin-presence + wrapper-entry assertions
Typed mergeRslibConfig composition, mutators return config, no as never unchanged
No direct @rspack/core dependency (value/types via @rslib/core) unchanged
Exact-match ($) framework aliases unchanged
cleanDistPath: false pinned in final merge fragment + asserted unchanged (sibling-safety justification)
Build handles closed in finally unchanged

New fail-closed assertions: a resolved environment missing the plugin instance or the redirected wrapper entry aborts before the build; a hostile hatch that strips config.plugins and repoints config.entry is overridden by the framework hook (proven by a wrapper-only marker in the emitted bundle).

Gate results (all local, this branch)

  • build / typecheck / lint: clean (0 errors, 0 warnings)
  • unit: 1775 tests — 1771 passed, 4 env-gated skips
  • targeted integration (build, hooks, package-build, dev-package-build, plugin-bundle): 65 + 22 passed; dev-package-build-service.test.ts re-run separately (11 passed; known include-list gap)
  • test:packed incl. scaffolder e2e: 23 tests — 22 passed, 1 env-gated skip
  • examples: audiobook-curator + mcp-app check both green

Independent pre-PR regression review (GPT Sol) focused on lost #79 invariants: no blockers; two should-fixes (hostile-hatch/fail-closed coverage, changeset headline truthfulness) addressed in the second commit; re-review verdict SHIP.

Changesets: agent-bundle patch for the transport change; the unreleased #79 changeset's transport/dist-cleaning bullets amended so combined release notes describe shipped behavior.

… at dedicated virtual paths

Architecture reversal of the generated-source transport from PR #79's
on-disk materialization, preserving all its conformance invariants:
generated wrappers and registry modules keep their deterministic paths
under the reserved .agent-bundle-virtual/ namespace but are now
guaranteed-nonexistent module ids served from memory. Rslib validates
source.entry existence on the real filesystem, so the profile keys each
entry on the authored program and the invariant hook redirects the
lowered Rspack entry to the virtual wrapper path — no real path is ever
shadowed (the old entryAnchor overlay stays dead). A narrow feature
check turns an upstream plugin removal into an actionable diagnostic.
…; truthful changeset headline

Sol review should-fixes: a hostile tools hatch that strips plugins and
repoints the entry is overridden by the framework hook (merged last);
resolved environments missing the virtual-module plugin or the wrapper
entry fail closed before the build; the conformance changeset headline
no longer claims fully documented surfaces now that the virtual-module
transport deliberately rides one experimental plugin behind a feature
check.
@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 3da3aa8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T12:05:53.915937Z 8f8a630 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Aug 31, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@80
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@80
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/rsc-runtime@80

commit: 3da3aa8

…wing

Reuse the core isRecord guard for entry-record and descriptor narrowing
(dropping two casts and the self-recursive entryImportsOf trick), inline
the one-shot plannedVirtualModules helper at its composition site, hoist
the per-entry registry-module computation in assertExecutableConfig, and
drop the patch changeset that described replacing disk materialization no
release ever shipped (the amended minor changeset already documents the
virtual transport).
@ScriptedAlchemy
ScriptedAlchemy merged commit 560124a into main Aug 31, 2026
9 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the refactor/virtual-generated-entries branch August 31, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant